package com.abc.filter;

import com.abc.util.AESUtil;
import com.abc.util.RSAUtils;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.List;
import java.util.Map;

/**
 * 第二步：请求参数解密处理
 *
 * gateway中我们并不能直接对请求参数进行修改，那么如何才能将加密的请求一解密后的方式路由到下游的服务中去呢？这里我们需要在filter中根据原来的请求对参数解密后创建新的请求。需要注意的是：解密后对请求头CONTENT_LENGTH需要重置，都在会出现读取的参数不完整的情况。
 * 如果需要对参数进行解密处理，所以对于客户端的请求统一使用POST的请求方式。
 * 加密解密的方法根据你的需要处理。(此处我使用的是RSA,AES结合的方式-之后会继续分享此种加密方式)
 *
 * @Description APP端,请求参数解密
 */
public class AppReqDecryptFilter implements GatewayFilter, Ordered {

    private static final Logger logger = LoggerFactory.getLogger(AppReqDecryptFilter.class);

    private int order;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 设置是否加密标识
        List<String> bgDebugHeaders = exchange.getRequest().getHeaders().get("bg-debug");
        String bgDebug = bgDebugHeaders != null ? bgDebugHeaders.get(0) : ConstantFilter.REQ_RES_ENCRYPT;
        exchange.getAttributes().put(ConstantFilter.BG_DEBUG_KEY, bgDebug);

        // 获取请求的方法
        ServerHttpRequest oldRequest = exchange.getRequest();
        String method = oldRequest.getMethodValue();
        URI uri = oldRequest.getURI();

        if ("POST".equals(method)){
            // 尝试从 exchange 的自定义属性中取出缓存到的 body
            Object cachedRequestBodyObject = exchange.getAttributeOrDefault(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, null);
            if (cachedRequestBodyObject != null) {
                byte[] decrypBytes;
                try {
                    byte[] body = (byte[]) cachedRequestBodyObject;
                    String rootData = new String(body); // 客户端传过来的数据
                    decrypBytes = body;

                    if(ConstantFilter.REQ_RES_ENCRYPT.equals(bgDebug)) {
                        JSONObject jsonObject = JSONObject.parseObject(rootData);
                        String encryptKey = (String) jsonObject.get("k");
                        String encryptData = (String) jsonObject.get("v");
                        //RSA私钥解密（首先前端AES加密，将AES密钥通过RSA公钥加密送给后端，后端需要RSA私钥来解密，得到AES密钥）
                        //后端生成RSA密钥对
//                        String key = RSAUtils.privateDecrypt(encryptKey);
                        String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKDdVfWy9XWFbyguc1Q6G2lMf7aiAmRq94tENw3jpGR2hfpYLIUVWY8mOTmYWLC9NW5jtjdZorkY/rpzbsSx1TlUZtgnF53PWdKDHUKv1sMrcVlE3A3T7SqzFBxMQByJ1Coye1lPe5/Gwsv4Lzd57ei215TvH1sJPTcGsy4HZGaHAgMBAAECgYAKJkzxJqx0EMo1p9ChBNWL7Ea2mDw8Z79cqx4qIy3RJOVJKV0NvI4dCRVdO+6EoDuJzoLFtUMeGKSh0PyxLmM8lCFQIgtiSu8l+iwe4IOoyBwAvS7EgCVdvCnOCE1CA0F/8FSY/b2RzNR+bbVcGAo/TgRuyTO4Mja+2AW4SkHJOQJBANWv975pVzrlz9EOFTaUd/U3taandZ/PYtB6KspRTMzXMVQKNirH5y+rDjBxj2V8AUVx3N9TIgHnIC8sv8tbJMsCQQDAt7ogRn4n5Y9WHbk6ctA1vkLHSF0VmkpMP8ogOFd56Egv0jFse8hKbeeehYQVyS0zIbEGyhfZR0n+jkYeMMm1AkAZRPPhGofsORz4NdggWjJNV0ct7AOm4FlpGelRf4e7eX8l1wdMy9L0+h91bxhsAdwMoEhkSGpvGyvdKEeN/rs9AkEAqnXYPBQeIkv6rME9xWUopWDU0FBBhYojja4eqsErHrlGoIXbEYmrb6om5SBT5LE9OazjWI4I6jcUTASsIKgmwQJAf3sRcw4R/pIpuHtOfZaLjeHcBb/pd86kK8aDuLCa3uyefcQ46PEigwiJKlj7f2/cshA+Bz6qqi753f26658IeQ==";
                        String key = RSAUtils.decryptByPrivateKey(privateKey, encryptKey);
                        //AES密钥解密
//                        String decryptData = AESUtil.AESDecrypt(encryptData, key, "CBC");
                        String decryptData = AESUtil.decrypt(encryptData, key);
                        decrypBytes = decryptData.getBytes();
                    }

                }catch (Exception e){
                    logger.error("客户端数据解析异常:{}", e.toString());
//                    throw new AppException(ErrorCode.SYS_PARAMS_ERROR.code(), "客户端数据解析异常");
                    throw new RuntimeException("客户端数据解析异常");
                }

                // 根据解密后的参数重新构建请求
                DataBufferFactory dataBufferFactory = exchange.getResponse().bufferFactory();
                Flux<DataBuffer> bodyFlux = Flux.just(dataBufferFactory.wrap(decrypBytes));
                ServerHttpRequest newRequest = oldRequest.mutate().uri(uri).build();
                newRequest = new ServerHttpRequestDecorator(newRequest) {
                    @Override
                    public Flux<DataBuffer> getBody() {
                        return bodyFlux;
                    }
                };

                // 构建新的请求头
                HttpHeaders headers = new HttpHeaders();
                headers.putAll(exchange.getRequest().getHeaders());
                // 由于修改了传递参数，需要重新设置CONTENT_LENGTH，长度是字节长度，不是字符串长度
                int length = decrypBytes.length;
                headers.remove(HttpHeaders.CONTENT_LENGTH);
                headers.setContentLength(length);
                // headers.set(HttpHeaders.CONTENT_TYPE, "application/json");
                newRequest = new ServerHttpRequestDecorator(newRequest) {
                    @Override
                    public HttpHeaders getHeaders() {
                        return headers;
                    }
                };

                // 把解密后的数据重置到exchange自定义属性中,在之后的日志GlobalLogFilter从此处获取请求参数打印日志
                exchange.getAttributes().put(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, decrypBytes);
                return chain.filter(exchange.mutate().request(newRequest).build());
            }
        }else if("GET".equals(method)){ // todo 暂不处理
            Map requestQueryParams = oldRequest.getQueryParams();
            return chain.filter(exchange);
        }
        return chain.filter(exchange);
    }


    @Override
    public int getOrder() {
        return this.order;
    }


    public AppReqDecryptFilter(int order){
        this.order = order;
    }
}